import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.Box;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;

import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.keyboard.NativeKeyEvent;
import org.jnativehook.keyboard.NativeKeyListener;
import org.jnativehook.mouse.NativeMouseEvent;
import org.jnativehook.mouse.NativeMouseMotionListener;

public class MainFrame extends JFrame implements NativeKeyListener, NativeMouseMotionListener{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    
    private JTextArea tipArea = null;
    
    private Rectangle selectedArea = null;
    
    private Robot robot = null;
    
    private Thread playThread = null;
    
    private boolean playing = false;
    
    private Object playSync = new Object();
    
    @SuppressWarnings("unused")
	private Point mousePoint = null;
    
    public MainFrame() {
        setTitle("The color auto player");
        setLayout(new BorderLayout());
        createTipArea();
        getContentPane().setBackground(Color.WHITE);
        getContentPane().add(tipArea, BorderLayout.CENTER);
        getContentPane().add(Box.createVerticalStrut(5), BorderLayout.NORTH);
        getContentPane().add(Box.createVerticalStrut(5), BorderLayout.SOUTH);
        getContentPane().add(Box.createHorizontalStrut(5), BorderLayout.WEST);
        getContentPane().add(Box.createHorizontalStrut(5), BorderLayout.EAST);
        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setAlwaysOnTop(true);
        setVisible(true);
        
        initGlobalListener();
        
        try {
            robot = new Robot();
        } catch (AWTException e) {
            JOptionPane.showMessageDialog(this, e.getMessage());
            System.exit(-1);
        }
    }
    
    private void createTipArea() {
        tipArea = new JTextArea(10, 20);
        tipArea.setEditable(false);
        
        StringBuffer buffer = new StringBuffer();
        buffer.append("Hot keys:\n");
        buffer.append("a: select game location\n");
        buffer.append("k: play once\n");
        buffer.append("u: start play\n");
        buffer.append("o: stop play\n");
        buffer.append("q: quit game\n");
        tipArea.setText(buffer.toString());
    }
    
    private void initGlobalListener() {
        Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
        logger.setLevel(Level.OFF);
        try {
            GlobalScreen.registerNativeHook();
        } catch (NativeHookException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.exit(1);
        }
        
        /* Note: JNativeHook does *NOT* operate on the event dispatching thread.
         * Because Swing components must be accessed on the event dispatching
         * thread, you *MUST* wrap access to Swing components using the
         * SwingUtilities.invokeLater() or EventQueue.invokeLater() methods.
         */
//        GlobalScreen.setEventDispatcher(new SwingDispatchService());
        GlobalScreen.addNativeKeyListener(this);
        GlobalScreen.addNativeMouseMotionListener(this);
    }
    
    private void selectArea() {
        selectedArea = AreaSelector.getSelection();
    }
    
    private boolean playOnce() {
        if(selectedArea == null) {
            JOptionPane.showMessageDialog(this, 
                    "Please select an area first", "Error", JOptionPane.ERROR_MESSAGE);
            return false;
        } 
        
//        long begMillis, endMillis;
//        
//        begMillis = System.currentTimeMillis();

        BufferedImage image = robot.createScreenCapture(selectedArea);
        Detector detector = new Detector();
        Point locPoint = detector.detect(image);
        
//        endMillis = System.currentTimeMillis();
//        System.out.println("total spend:" + (endMillis - begMillis) + "ms");
        
        if(locPoint == null) {
            JOptionPane.showMessageDialog(this, 
                    "Nothing is detected", "Error", JOptionPane.ERROR_MESSAGE);
            return false;
        }
        
        robot.mouseMove(selectedArea.x + locPoint.x, selectedArea.y + locPoint.y);
        robot.mousePress(InputEvent.BUTTON1_MASK);
//        try {
//            Thread.sleep(10);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        robot.mouseRelease(InputEvent.BUTTON1_MASK);
        return true;
    }
    
    private void startPlay() {
        if(selectedArea == null) {
            JOptionPane.showMessageDialog(this, 
                    "Please select an area first", "Error", JOptionPane.ERROR_MESSAGE);
            return;
        }
        
        synchronized (playSync) {
            if(playing) {
                System.out.println("Already in playing");
                return;
            }
            
            playThread = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    while(playing) {
                    	if(!playOnce()) {
                    		playing = false;
                        	break;
                        }
                    }
                }
            });
            playing = true;
            playThread.start();
        }
    }
    
    private void stopPlay() {
        synchronized (playSync) {
            playing = false;
            if(playThread != null) {
                playThread.interrupt();
                try {
                    playThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                playThread = null;
            }
        }
    }
    
    public void sleep(long millis) {
    	try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
    }
    
    @Override
    public void nativeKeyTyped(NativeKeyEvent nativeEvent) {
//        System.out.println("nativeKeyTyped" + nativeEvent);
    }

    @Override
    public void nativeKeyPressed(NativeKeyEvent nativeEvent) {
        
//        System.out.println("nativeKeyPressed" + nativeEvent);
    }

    @Override
    public void nativeKeyReleased(NativeKeyEvent nativeEvent) {
//        System.out.println("nativeKeyReleased" + nativeEvent);

        switch (nativeEvent.getKeyCode()) {
        case NativeKeyEvent.VC_A:
            System.out.println("Type a");
            selectArea();
            break;
        case NativeKeyEvent.VC_K:
            System.out.println("Type k");
            playOnce();
            break;
        case NativeKeyEvent.VC_U:
            System.out.println("Type u");
            startPlay();
            break;
        case NativeKeyEvent.VC_O:
            System.out.println("Type o");
            stopPlay();
            break;
        case NativeKeyEvent.VC_Q:
        	System.exit(0);
        	break;
        }
    }

    @Override
    public void nativeMouseMoved(NativeMouseEvent nativeEvent) {
        // TODO Auto-generated method stub
//        System.out.println(nativeEvent);
    	mousePoint = nativeEvent.getPoint();
    }

    @Override
    public void nativeMouseDragged(NativeMouseEvent nativeEvent) {
        // TODO Auto-generated method stub
        
    }
}
